﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace RetryUtil.ZhaoXuan
{
    internal class Retrier
    {
        /// <summary>
        /// 当前运行模式（异步、同步）
        /// </summary>
        public bool IsAsyn { get; private set; }

        private System.Threading.Timer _timer;
        private System.Threading.Timer _stopTimer;

        /// <summary>
        /// 是否执行成功
        /// </summary>
        public bool IsSuccess { get; private set; }

        /// <summary>
        /// 重试时间间隔的增长模式
        /// </summary>
        private double[] _increaseMode { get; set; }

        /// <summary>
        /// 重试次数
        /// </summary>
        private uint _retryTimes { get; set; }

        /// <summary>
        /// 重试间隔周期（毫秒）
        /// </summary>
        private uint _period { get; set; }

        /// <summary>
        /// 是否运行中
        /// </summary>
        private bool _isRunning { get; set; }

        /// <summary>
        /// 是否取消执行
        /// </summary>
        private bool _isToCancel { get; set; }

        /// <summary>
        /// 超时时间（毫秒）
        /// </summary>
        private uint _timeout { get; set; } = 0;

        public Retrier(double[] increaseMode = null, uint retryTimes = int.MaxValue, uint period = 60000, uint timeout = 0)
        {
            this._increaseMode = increaseMode?.Length > 0 ? increaseMode.ToArray() : new double[] { 1 };
            this._retryTimes = retryTimes;
            this._period = period;
            this._timeout = timeout;
        }

        public void RunAsync(Func<bool> action, Action<bool> callBack = null)
        {
            this.IsAsyn = true;
            if (_isRunning)
            {
                throw new Exception("正在运行，请勿重复执行");
            }
            int execTimes = 0;
            this._timer = new System.Threading.Timer(state =>
            {
                if (this._timer == null || this.IsSuccess || this._isToCancel)
                {
                    this._timer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                    this._timer?.Dispose();
                    this._timer = null;
                    this._isRunning = false;
                    return;
                }

                try
                {
                    this._timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                    execTimes++;
                    var result = action();
                    IsSuccess = true;
                    callBack(IsSuccess);
                }
                catch (Exception ex)
                {
                    // 记录日志
                    Console.WriteLine($"第{execTimes}/{this._retryTimes}次执行失败！{(execTimes >= this._retryTimes ? "" : $"将继续重试{this._retryTimes - execTimes}次！")}详情：{ex.Message}");
                    if (execTimes >= this._retryTimes)
                    {
                        callBack(IsSuccess);
                        throw ex;
                    }
                }
                finally
                {
                    if (execTimes <= this._retryTimes && !this.IsSuccess)
                    {
                        this._timer?.Change((int)(this._period * this._increaseMode[(execTimes % this._increaseMode.Length)]), System.Threading.Timeout.Infinite);
                    }
                    else
                    {
                        this._timer.Dispose();
                        this._timer = null;
                        this._isRunning = false;
                    }
                }
            }, null, 0, System.Threading.Timeout.Infinite);

            if (this._timeout > 0)
            {
                if (this._stopTimer == null)
                {
                    this._stopTimer = new System.Threading.Timer(state =>
                    {
                        this._stopTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                        this.Stop();
                    }, null, 0, (int)this._timeout);
                }
                else
                {
                    this._stopTimer.Change(0, (int)this._timeout);
                }
            }
        }

        public void RunSync(Func<bool> action)
        {
            this.IsAsyn = false;
            if (_isRunning)
            {
                throw new Exception("正在运行，请勿重复执行");
            }
            this._isRunning = true;
            for (int execTimes = 1; execTimes <= this._retryTimes; execTimes++)
            {
                if (this.IsSuccess || this._isToCancel)
                {
                    this._isRunning = false;
                    return;
                }
                try
                {
                    action();
                    IsSuccess = true;
                }
                catch (Exception ex)
                {
                    // 记录日志
                    Console.WriteLine($"第{execTimes}/{this._retryTimes}次执行失败！{(execTimes > this._retryTimes ? "" : $"将继续重试{this._retryTimes - execTimes}次！")}详情：{ex.Message}");
                    if (execTimes >= this._retryTimes)
                    {
                        throw ex;
                    }
                }
                finally
                {
                    Thread.Sleep((int)(this._period * this._increaseMode[(execTimes % this._increaseMode.Length)]));
                }
            }

            this._isRunning = false;
        }

        public void Stop()
        {
            this._isToCancel = true;
            if (this._timer != null)
            {
                this._timer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                this._timer?.Dispose();
                this._timer = null;
                this._isRunning = false;
                Console.WriteLine("已停止！");
                return;
            }
        }
    }

    internal class RetrierTest
    {
        public static void Test()
        {
            int count = 0;
            bool isInitSuccess = false;
            try
            {
                var retrier = new Retrier(new double[] { 2, 3, 4 }, retryTimes: 5, period: 1000);
                retrier.RunAsync(() =>
                {
                    Console.WriteLine($"时间：{DateTime.Now.ToString("ss:fff")}");
                    if (count++ <= 3)
                    {
                        throw new Exception("初始化失败！");
                    }
                    Console.WriteLine("初始化成功！！！");
                    return true;
                }, isSuccess =>
                {
                    isInitSuccess = isSuccess;
                    Console.WriteLine($"初始化结束！！！执行{(isSuccess ? "Success" : "Fail")}");
                });

                Thread.Sleep(5000);
                retrier.Stop();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"初始化失败，请手工处理！详情：{ex.Message}");
            }
            Console.ReadKey();
            return;
        }
    }

    // 初始化的过程可能较为复杂
    //  其中可能存在需重试“n次”，或者一直重试直至超时，或者不重要的无需重试的部分
    //      ：设定超时时间
    //      ：
    //      ：不需要重试的部分让外部自己处理
    //  重试的间隔可能是不同的：设定重试模式
    //  初始化过程的子步骤可能需要各自重试：此时应是多个Retrier实例
    // 

    internal class AppInitionHelper
    {
        public static void Test()
        {
            int count = 0;
            bool isInitSuccess = false;
            try
            {
                AppInition(() =>
                {
                    Console.WriteLine($"时间：{DateTime.Now.ToString("ss:fff")}");
                    if (count++ <= 10)
                    {
                        throw new Exception("初始化失败！");
                    }
                    Console.WriteLine("初始化成功！！！");
                }, isSuccess =>
                {
                    isInitSuccess = isSuccess;
                    Console.WriteLine($"初始化结束！！！执行{(isSuccess ? "Success" : "Fail")}");
                }, increaseMode: new double[] { 2, 3, 4 }, times: 5, period: 1000);// 一秒重试一次
            }
            catch (Exception ex)
            {
                Console.WriteLine($"初始化失败，请手工处理！详情：{ex.Message}");
            }
            Console.ReadKey();
            return;
        }
        /// <summary>
        /// 初始化操作（失败支持重试）
        ///     Timer版本
        /// </summary>
        /// <param name="Inition">要执行的初始化操作</param>
        /// <param name="increaseMode">增长模式：为空时，期间固定为1倍；有1位时，表示每次增长固定倍数；有多位时，循环增长倍数</param>
        /// <param name="times">重试次数，为0时不重试</param>
        /// <param name="period">每次执行间隔周期，单位毫秒</param>
        private static void AppInition(Action Inition, Action<bool> callBack, double[] increaseMode = null, uint times = 5, uint period = 60000)
        {
            if (Inition == null)
            {
                return;
            }
            var modeArray = increaseMode?.Length > 0 ? increaseMode : new double[] { 1 };

            bool isInitSuccess = false;
            int execTimes = 0;
            System.Threading.Timer timer = null;
            timer = new System.Threading.Timer(state =>
            {
                if (timer == null || isInitSuccess)
                {
                    return;
                }

                try
                {
                    timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                    execTimes++;
                    Inition();
                    isInitSuccess = true;
                    callBack(isInitSuccess);
                }
                catch (Exception ex)
                {
                    // 记录日志
                    Console.WriteLine($"第{execTimes}/{times}次执行失败！{(execTimes >= times ? "" : $"将继续重试{times - execTimes}次！")}详情：{ex.Message}");
                    if (execTimes >= times)
                    {
                        callBack(isInitSuccess);
                        throw ex;
                    }
                }
                finally
                {
                    if (execTimes <= times && !isInitSuccess)
                    {
                        timer.Change((int)(period * modeArray[(execTimes % modeArray.Length)]), System.Threading.Timeout.Infinite);
                    }
                    else
                    {
                        timer.Dispose();
                        timer = null;
                    }
                }
            }, null, 0, System.Threading.Timeout.Infinite);
        }

        /// <summary>
        /// 初始化操作（失败支持重试）
        ///     Sleep版
        /// </summary>
        /// <param name="Inition">要执行的初始化操作</param>
        /// <param name="increaseMode">增长模式：为空时，期间固定为1倍；有1位时，表示每次增长固定倍数；有多位时，循环增长倍数</param>
        /// <param name="times">重试次数，为0时不重试</param>
        /// <param name="period">每次执行间隔周期，单位毫秒</param>
        private static void AppInitionN(Action Inition, double[] increaseMode = null, uint times = 5, uint period = 60000)
        {
            if (Inition == null)
            {
                return;
            }
            var modeArray = increaseMode?.Length > 0 ? increaseMode : new double[] { 1 };

            bool isInitSuccess = false;
            int execTimes = 1;
            for (; execTimes <= times; execTimes++)
            {
                if (isInitSuccess)
                {
                    return;
                }
                try
                {
                    Inition();
                    isInitSuccess = true;
                }
                catch (Exception ex)
                {
                    // 记录日志
                    Console.WriteLine($"第{execTimes}/{times}次执行失败！{(execTimes > times ? "" : $"将继续重试{times - execTimes}次！")}详情：{ex.Message}");
                    if (execTimes >= times)
                    {
                        throw ex;
                    }
                }
                finally
                {
                    Thread.Sleep((int)(period * modeArray[(execTimes % modeArray.Length)]));
                }
            }
        }
    }

}
